/* $Id: View.java 63 2010-02-14 13:52:34Z aaron.day $ */
package org.crazydays.gameplan.view;


import java.beans.PropertyVetoException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JMenuBar;
import javax.swing.JRootPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.crazydays.gameplan.controller.ActionController;
import org.crazydays.gameplan.db.Database;
import org.crazydays.gameplan.db.swing.JDatabaseTreePane;
import org.crazydays.gameplan.map.GameMap;
import org.crazydays.gameplan.map.swing.JMapFrame;
import org.crazydays.gameplan.map.swing.MapFrameFactory;
import org.crazydays.gameplan.map.swing.MapMouseListener;
import org.crazydays.gameplan.model.IDatabaseObserver;
import org.crazydays.gameplan.model.Model;
import org.crazydays.gameplan.swing.JLogoDesktopPane;
import org.crazydays.gameplan.util.IconHelper;
import org.crazydays.gameplan.view.paint.JPaintPane;
import org.crazydays.i18n.Messages;

/**
 * View
 */
public class View
    extends JRootPane
    implements IDatabaseObserver, InternalFrameListener
{
    /** serial version uid */
    public final static long serialVersionUID =
        "$Id: View.java 63 2010-02-14 13:52:34Z aaron.day $".hashCode();

    /** logger */
    protected final static Logger LOGGER = LoggerFactory.getLogger(View.class);

    /** messages */
    protected final static Messages MESSAGES = Messages.getInstance();

    /** model */
    protected Model model;

    /** action controller */
    protected ActionController actionController;

    /** file menu */
    protected FileMenu fileMenu;

    /** edit menu */
    protected EditMenu editMenu;

    /** view menu */
    protected ViewMenu viewMenu;

    /** help menu */
    protected HelpMenu helpMenu;

    /** split pane */
    protected JSplitPane contentPane;

    /** control pane */
    protected JTabbedPane controlPane;

    /** paint pane */
    protected JPaintPane paintPane;

    /** database tree pane */
    protected JDatabaseTreePane databaseTreePane;

    /** desktop pane */
    protected JDesktopPane desktopPane;

    /** map frame factory */
    protected MapFrameFactory mapFrameFactory;

    /** open game maps */
    protected Map<String, JMapFrame> openGameMaps =
        new HashMap<String, JMapFrame>();

    /** map frame mouse listener */
    protected MapMouseListener mapMouseListener;

    /**
     * Set model.
     * 
     * @param model Model
     */
    public void setModel(Model model)
    {
        this.model = model;
    }

    /**
     * Set action controller.
     * 
     * @param actionController ActionController
     */
    public void setActionController(ActionController actionController)
    {
        this.actionController = actionController;
    }

    /**
     * Set file menu.
     * 
     * @param fileMenu FileMenu
     */
    public void setFileMenu(FileMenu fileMenu)
    {
        this.fileMenu = fileMenu;
    }

    /**
     * Set edit menu.
     * 
     * @param editMenu EditMenu
     */
    public void setEditMenu(EditMenu editMenu)
    {
        this.editMenu = editMenu;
    }

    /**
     * Set view menu.
     * 
     * @param viewMenu ViewMenu
     */
    public void setViewMenu(ViewMenu viewMenu)
    {
        this.viewMenu = viewMenu;
    }

    /**
     * Set help menu.
     * 
     * @param helpMenu HelpMenu
     */
    public void setHelpMenu(HelpMenu helpMenu)
    {
        this.helpMenu = helpMenu;
    }

    /**
     * Set database tree pane.
     * 
     * @param databaseTreePane DatabaseTreePane
     */
    public void setDatabaseTreePane(JDatabaseTreePane databaseTreePane)
    {
        this.databaseTreePane = databaseTreePane;
    }

    /**
     * Set paint pane.
     * 
     * @param paintPane PaintPane
     */
    public void setPaintPane(JPaintPane paintPane)
    {
        this.paintPane = paintPane;
    }

    /**
     * Set map frame factory.
     * 
     * @param mapFrameFactory MapFrameFactory
     */
    public void setMapFrameFactory(MapFrameFactory mapFrameFactory)
    {
        this.mapFrameFactory = mapFrameFactory;
    }

    /**
     * Initialize.
     */
    public void initialize()
    {
        model.addDatabaseObserver(this);

        initializeMenuBar();
        initializeContentPane();
    }

    /**
     * Initialize menubar.
     */
    protected void initializeMenuBar()
    {
        setJMenuBar(new JMenuBar());
        getJMenuBar().add(fileMenu);
        getJMenuBar().add(editMenu);
        getJMenuBar().add(viewMenu);
        getJMenuBar().add(helpMenu);
    }

    /**
     * Initialize content pane.
     */
    public void initializeContentPane()
    {
        initializeControlPane();
        initializeDesktopPane();

        contentPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
        contentPane.setOneTouchExpandable(true);
        contentPane.setDividerLocation(200);
        contentPane.setLeftComponent(controlPane);
        contentPane.setRightComponent(desktopPane);

        setContentPane(contentPane);
    }

    /**
     * Initialize control pane.
     */
    protected void initializeControlPane()
    {
        controlPane = new JTabbedPane();

        controlPane.addTab(MESSAGES.getMessage(JDatabaseTreePane.RESKEY_NAME),
            IconHelper.loadIcon(MESSAGES
                .getMessage(JDatabaseTreePane.RESKEY_ICON)), databaseTreePane);

        controlPane.addTab(MESSAGES.getMessage(JPaintPane.RESKEY_NAME),
            IconHelper.loadIcon(MESSAGES.getMessage(JPaintPane.RESKEY_ICON)),
            paintPane);
    }

    /**
     * Initialize desktop pane.
     */
    protected void initializeDesktopPane()
    {
        desktopPane = new JLogoDesktopPane();
    }

    /**
     * Open game map and bring it to the front.
     * 
     * @param gameMap GameMap
     */
    public void open(GameMap gameMap)
    {
        if (gameMap == null) {
            throw new IllegalArgumentException("gameMap == null");
        }

        String key = gameMap.getName();

        JMapFrame frame = openGameMaps.get(key);
        if (frame == null) {
            frame = mapFrameFactory.buildMapFrame(gameMap);
            frame.addInternalFrameListener(this);

            desktopPane.add(frame);
            openGameMaps.put(key, frame);
        }

        desktopPane.moveToFront(frame);

        try {
            frame.setSelected(true);
        } catch (PropertyVetoException e) {
            LOGGER.warn("Unable to set selected frame {0} {1}", key, e);
        }
    }

    /**
     * @param database Database
     * @see IDatabaseObserver#opened(Database)
     */
    @Override
    public void opened(Database database)
    {
    }

    /**
     * Close all open artifacts.
     * 
     * @param database Database
     * @see IDatabaseObserver#closed(Database)
     */
    @Override
    public void closed(Database database)
    {
        // close internal frames
        for (JInternalFrame frame : desktopPane.getAllFrames()) {
            frame.dispose();
        }

        // close maps
        openGameMaps.clear();
    }

    /**
     * Internal frame opened.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameOpened(InternalFrameEvent)
     */
    @Override
    public void internalFrameOpened(InternalFrameEvent event)
    {
    }

    /**
     * Enable map options.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameActivated(InternalFrameEvent)
     */
    @Override
    public void internalFrameActivated(InternalFrameEvent event)
    {
        JInternalFrame frame = event.getInternalFrame();

        if (frame instanceof JMapFrame) {
            actionController.enableMapOptions();

            JMapFrame mapFrame = (JMapFrame) frame;
            if (mapFrame.isGridVisible()) {
                actionController.getToggleGrid().setIconCross();
            } else {
                actionController.getToggleGrid().setIconTick();
            }
        }
    }

    /**
     * Internal frame iconified.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameIconified(InternalFrameEvent)
     */
    @Override
    public void internalFrameIconified(InternalFrameEvent event)
    {
    }

    /**
     * Internal frame deiconified.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameDeiconified(InternalFrameEvent)
     */
    @Override
    public void internalFrameDeiconified(InternalFrameEvent event)
    {
    }

    /**
     * Disable map options.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameDeactivated(InternalFrameEvent)
     */
    @Override
    public void internalFrameDeactivated(InternalFrameEvent event)
    {
        JInternalFrame frame = event.getInternalFrame();

        if (frame instanceof JMapFrame) {
            actionController.disableMapOptions();
        }
    }

    /**
     * Internal frame closing.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameClosing(InternalFrameEvent)
     */
    @Override
    public void internalFrameClosing(InternalFrameEvent event)
    {
    }

    /**
     * Remove game map from open game maps.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameClosed(InternalFrameEvent)
     */
    @Override
    public void internalFrameClosed(InternalFrameEvent event)
    {
        JInternalFrame frame = event.getInternalFrame();
        if (frame instanceof JMapFrame) {
            openGameMaps.remove(((JMapFrame) frame).getGameMap().getName());
        }

        frame.removeInternalFrameListener(this);
    }

    /**
     * Toggle map grid.
     * 
     * @return Grid state
     */
    public boolean toggleGrid()
    {
        JMapFrame mapFrame = getActiveMapFrame();

        if (mapFrame == null) {
            return false;
        }

        mapFrame.setGridVisible(!mapFrame.isGridVisible());
        return mapFrame.isGridVisible();
    }

    /**
     * Get active map frame.
     * 
     * @return JMapFrame
     */
    public JMapFrame getActiveMapFrame()
    {
        JInternalFrame frame = desktopPane.getSelectedFrame();

        if (!(frame instanceof JMapFrame)) {
            LOGGER.warn("Selected frame not JMapFrame");
            return null;
        }

        return (JMapFrame) frame;
    }
}
